package org.mySpring.core;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.dom4j.util.StringUtils;

import javax.xml.parsers.SAXParser;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @program: mySSM
 * @description: 类路径资源文件应用上下文
 * @author:wjp
 * @create: 2025-02-15 16:54
 **/
public class ClassPathApplicationContext implements ApplicationContext{

    /**
     * 配置文件存放位置，仅支持类路径，此处只支持一个配置文件路径
     */
    private String configLocation;

    /**
     * 类Spring三级缓存，singletonObjects，存放单例对象
     */
    private final Map<String,Object> singletonObjects = new HashMap<String,Object>();

    public ClassPathApplicationContext(String configLocation) throws Exception {
        // 初始化ClassPathApplicationContext对象时扫描配置文件进行bean的实例化

        SAXReader saxReader = new SAXReader();
        // 获取类路径下的资源文件流
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
        // 配置文件document对象
        Document document = saxReader.read(in);
        // "//bean"语法代表获取xml文档对象中的所有bean标签节点
        List<Node> nodeList = document.selectNodes("//bean");
        // 遍历所有的node节点
        nodeList.forEach( node -> {
            // 向下转型为element对象
            Element beanElement = (Element) node;

            String beanId = beanElement.attributeValue("id");
            String className = beanElement.attributeValue("class");

            // 调用无参构造创建对象对放入map中
            try {
                Class<?> aClass = Class.forName(className);
                Constructor<?> noArgsConstructor = aClass.getDeclaredConstructor();
                Object o = noArgsConstructor.newInstance();
                singletonObjects.put(beanId, o);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

        // 此时只进行了bean对象的暴露，尚未对bean内部属性值进行赋值，因此需重新对所有的对象进行赋值
        nodeList.forEach(node -> {
            Element beanElement = (Element) node;

            // 获取beanId以便取出对应的对象
            String beanId = beanElement.attributeValue("id");

            // 取出对象
            Object obj = singletonObjects.get(beanId);

            // 获取当前element元素下所有的property标签
            List<Element> properties = beanElement.elements("property");

            // 遍历properties，该对象的属性配置
            properties.forEach( propertyElement -> {
                String filedName = propertyElement.attributeValue("name");
                String fieldValue = propertyElement.attributeValue("value");
                String fieldRef = propertyElement.attributeValue("ref");

                // 获取当前字段的set方法
                try {
                    // 需要根据字段类型判断方法的参数类型
                    Class<?> filedTypeClass = obj.getClass().getDeclaredField(filedName).getType();
                    String filedTypeSimpleName = filedTypeClass.getSimpleName();

                    Method declaredMethod = obj.getClass().getDeclaredMethod("set" + filedName.toUpperCase().charAt(0) + filedName.substring(1),filedTypeClass);


                    // 如果fieldValue不为空则为基本类型
                    if (fieldValue != null){
                        Object actuallyValue = null;
                        // 由于从xml中读取的attributeValue都会是String类型，因此需要将其判断转为真正的类型
                        switch (filedTypeSimpleName){
                            case "byte":
                                break;
                            case "short":
                                break;
                            case "int":
                                actuallyValue = Integer.parseInt(fieldValue);
                                break;
                            case "long":
                                break;
                            case "float":
                                break;
                            case "double":
                                actuallyValue = Double.parseDouble(fieldValue);
                                break;
                            case "char":
                                actuallyValue = fieldValue.charAt(0);
                                break;
                            case "Byte":
                                break;
                            case "Short":
                                break;
                            case "Integer":
                                break;
                            case "Long":
                                break;
                            case "Float":
                                break;
                            case "Double":
                                break;
                            case "Character":
                            default:
                                // 默认就为String类型
                                actuallyValue = fieldValue;
                        }
                        declaredMethod.invoke(obj,actuallyValue);
                    }

                    // 如果FieldRef不为空则为引用类型
                    if (fieldRef != null){
                        // 引用类型直接从singletonObjects中取出对应的对象放入即可
                        declaredMethod.invoke(obj,singletonObjects.get(fieldRef));
                    }

                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        });

    }

    @Override
    public Object getBean(String beanName) {
        return singletonObjects.get(beanName);
    }
}
